#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <assert.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <libxml/xmlwriter.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/parser.h>
#include <libxml/xpathInternals.h>


#define BIND_REQUEST 0
#define LOOKUP_REQUEST 1
#define MAX_BUF 400
#define NAMING_SERVICE "naming-service.xml"
#define XML_NODE_ROOT "naming_service"
#define XML_OBJECT_NAME "object_name"
#define XML_OBJECT_ADRESS "object_adress"

int  connection(int port);
void bind_request(int client_fd);
void lookup_request(int client_fd);
void xml_create_naming_service();
void xml_add_object(char object_name[MAX_BUF], unsigned long object_adress);
unsigned long  xml_find_object(char object_name[MAX_BUF]);

int main(int argc, char* argv[]){
  if(argc != 2){
    fprintf(stderr,"Usage: ./naming-server <port>\n");
    exit(1);
  }
  int port = atoi(argv[1]);
  int server_fd = connection(port);
  int client_fd;
  int message_type;
  
  xml_create_naming_service();
  while(1){
    client_fd = accept(server_fd,NULL,NULL);
    read(client_fd,&message_type,sizeof(int));
    if(message_type == BIND_REQUEST){
      bind_request(client_fd);
    }else if(message_type == LOOKUP_REQUEST){
      lookup_request(client_fd);
    }
  }
  return 0;
}

/*****************************************************************************/
int connection(int port){
  int fd = socket(AF_INET, SOCK_STREAM, 0);
  struct sockaddr_in addr;  

  assert(fd > 0);  
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = htonl(INADDR_ANY);      
  addr.sin_port = htons(port);            
  int err = bind(fd,(struct sockaddr*)&addr, sizeof(addr));
  assert(err >= 0);
  listen(fd, 4);
  fprintf(stderr, "server listening on port %d\n", port);
  return fd;
}

/*****************************************************************************/
void bind_request(int client_fd){
  char object_name[MAX_BUF];
  unsigned long  object_adress;
  int err = read(client_fd,object_name , MAX_BUF*sizeof(char));
  if (err < 0){
    perror("bind_request function!\n");
    exit(1);
  }
  err = read(client_fd,&object_adress , sizeof(unsigned long));
  if (err < 0){
    perror("bind_request function!\n");
    exit(1);
  }

  xml_add_object(object_name, object_adress);
}

/*****************************************************************************/
void lookup_request(int client_fd){
  char object_name[MAX_BUF];
  unsigned long  object_adress;
  int err = read(client_fd,object_name ,MAX_BUF*sizeof(char));
  if (err < 0){
    perror("lookup_request function!\n");
    exit(1);
  }
  
  object_adress = xml_find_object(object_name);
  write(client_fd, &object_adress, sizeof(unsigned long));
  
}

/*****************************************************************************/
void xml_create_naming_service(){
  xmlDocPtr doc;
  xmlNodePtr rootnode;
  
  doc = xmlNewDoc ("1.0");
  rootnode = xmlNewDocNode(doc, NULL, (const xmlChar*)XML_NODE_ROOT, NULL);
  xmlDocSetRootElement(doc, rootnode);
  xmlSaveFile(NAMING_SERVICE,doc);
  xmlFreeDoc(doc);
}

/*****************************************************************************/
void  xml_add_object(char object_name[MAX_BUF], unsigned long object_adress){
  xmlDocPtr doc;
  xmlNodePtr rootnode;
  xmlNodePtr object_node;
  char object_adress_[MAX_BUF];

  sprintf(object_adress_,"%ld",object_adress);
  doc = xmlParseFile(NAMING_SERVICE);
  if(doc == NULL){
    perror("xml_add_object function");
    exit(1);
  }
  rootnode = xmlDocGetRootElement(doc);
  if(rootnode == NULL){
    perror("xml_add_object function");
    exit(1);
  }
  object_node = xmlNewTextChild(rootnode, NULL, "object",NULL );
  if(object_node == NULL){
    perror("xml_add_object function");
    exit(1);
  }
  xmlNewProp(object_node, XML_OBJECT_NAME, (const xmlChar*)object_name );
  xmlNewProp(object_node, XML_OBJECT_ADRESS, (const xmlChar*)object_adress_ );
   
  xmlSaveFile(NAMING_SERVICE,doc);
  xmlFreeDoc(doc);
}


/*****************************************************************************/
unsigned long  xml_find_object(char object_name[MAX_BUF]){
  xmlXPathContextPtr context;
  xmlXPathObjectPtr result;
  xmlDocPtr doc;
  char xpath[MAX_BUF];
  xmlNodeSetPtr nodeset;
  char  object_adress[64] = "";

  sprintf(xpath,"//object[@%s='%s']", XML_OBJECT_NAME, object_name);
    
  doc = xmlParseFile(NAMING_SERVICE);
  context = xmlXPathNewContext(doc);
  result = xmlXPathEvalExpression((xmlChar *)xpath, context);
  nodeset = result->nodesetval;

  if(nodeset != NULL){
    strcpy(object_adress, xmlGetProp(nodeset->nodeTab[0],XML_OBJECT_ADRESS));
  }
  xmlXPathFreeObject(result);
  xmlFreeDoc(doc);
  
  return (unsigned long)atoi(object_adress);
}
